Goal = class("Goal")

Goal.State =
{
	Irrelevant = 1,		-- context makes goal irrelevant
	Potential = 2,		-- context makes goal potential
	Actual = 3			-- is an actual (reactive) goal
}

---

function Goal:initialize(name, parent)

	self.name = name
	self.parent = parent
	self.behavior = false
	self.state = Goal.State.Irrelevant
	self.updateVariablesFunction = false
	self.causes = false
	self.effects = false
	self.subGoals = {}
	self.entanglementsDescriptor = false
	
	
	-- variables repository
	
	self.variables = {}
	
	if parent
	then
		setmetatable(self.variables, {__index = parent.variables})
	end
end

---

function Goal:updateVariables()

	if self.updateVariablesFunction
	then
		self.updateVariablesFunction(self)
	end
	
	for goalName, goal in pairs(self.subGoals)
	do
		goal:updateVariables()
	end
end

---

function Goal:isEffectMatching(sentence)

	gLogger:log("trying to match with effect of " .. self.name)
	
	if self.effects
	then
		return self.effects:equal(sentence, self.variables), false
	else
	
		-- if we don't have effects, we look in subgoals below to find one
		
		local sameLevelMatch = false
		local subLevelMatch = false
		
		for subGoalName, subGoal in pairs(self.subGoals)
		do
			gLogger:log("trying to use " .. self.name .. " sub goals' effects of " .. subGoal.name)
			
			sameLevelMatch, subLevelMatch = subGoal:isEffectMatching(sentence)
			
			if sameLevelMatch
			then
				subLevelMatch = subGoal
				break
			end
		end 
		
		return false, subLevelMatch
	end
end

---

function Goal:getVariableNameAndValue(descriptor)

	local name, value
	
	if descriptor:sub(1, 1) == "@"
	then
		if descriptor:sub(2, 2) == "{"
		then
			name = descriptor:sub(3, descriptor:len() - 1)
			value = self.variables[name]
			
			assert(type(value) == "table", "goal vairable " .. descriptor .. " type error in goal " .. self.name)			
		else
			name = descriptor:sub(2, descriptor:len())
			value = self.variables[name]
		end
	else
		return "", descriptor
	end
	
	assert((value ~= nil), "goal vairable " .. descriptor .. " not found in goal " .. self.name)
	
	return name, value
end

---

function Goal:tryToRegress(intention, recursiveCall)

	gLogger:enterGroup(self.name)
	
	if not recursiveCall
	then
		Goal.alreadyTryedGoals = {}
	end
	
	Goal.alreadyTryedGoals[self.name] = true
		
	if self.name ~= Behavior.rootGoalName
	then
		intention.plan[#intention.plan + 1] = self
	
		if 
			self.causes and
			self.behavior.mind.beliefs:matchSentence(self.causes, self.variables)
		then
		
			-- my causes being true in current beliefs, we climb the hierarchy 
		
			gLogger:log("linking to parent goal because this goal causes is present in beliefs")
			gLogger:leaveGroup()
			
			return self.parent:tryToRegress(intention, true)
			
		elseif not self.causes
		then
		
			-- if i don't have causes, we directly climb the hierarchy
			 
			gLogger:log("linking to parent goal because this goal have no causes")
			gLogger:leaveGroup()
			
			return self.parent:tryToRegress(intention, true)
		else
			local sameLevelMatch
			local subLevelMatch = false
		
		
			-- try goals at the same hierarchical level
			
			for goalName, goalAtMyLevel in pairs(self.parent.subGoals)
			do
				if 
					(not Goal.alreadyTryedGoals[goalName])
				then
					sameLevelMatch, subLevelMatch = goalAtMyLevel:isEffectMatching(self.causes)
					
					if sameLevelMatch
					then
						gLogger:log("linking with a goal at the same level of the hierarchy")
						gLogger:leaveGroup()
						
						return goalAtMyLevel:tryToRegress(intention, true)
					end
					
					if subLevelMatch
					then
						break
					end
				end
			end
			
			
			-- try sub goal
			
			if subLevelMatch
			then
				gLogger:log("linking with a sub goal of a goal at the same level of hierarchy that had no effect")
				gLogger:leaveGroup()
				
				return subLevelMatch:tryToRegress(intention, true)
			end
			
			
			-- try parent goal
			
			if self.parent.causes	
			then
				gLogger:log ("linking with parent goal because no goal is matching at the same level of hierarchy")
				gLogger:leaveGroup()
				
				return self.parent:tryToRegress(intention, true)
			else
			
				-- failure
				
				gLogger:log ("Stoping because a goal with a causes can't link with a goal without causes")
				gLogger:leaveGroup()
				
				return false
			end
		end
	else
	
		-- we suceeded to reach to root goal of the behavior
		
		gLogger:log ("Suceeded to reach the Root goal")
		gLogger:leaveGroup()
		
		return true								
	end
end